package com.king.common.utils.quartz;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;

import com.king.common.constant.Constants;
import com.king.common.constant.ScheduleConstants;
import com.king.common.exception.AssertHelper;
import com.king.common.exception.job.TaskException;
import com.king.common.exception.job.TaskException.Code;
import com.king.common.utils.spring.SpringUtils;
import com.king.common.utils.text.StringUtils;
import com.king.modules.monitor.domain.SysJobEntity;

/**
 * 定时任务工具类
 * 
 * @author king
 *
 */
public class ScheduleUtils {
	/**
	 * 得到quartz任务类
	 *
	 * @param sysJob
	 *            执行计划
	 * @return 具体执行任务类
	 */
	private static Class<? extends Job> getQuartzJobClass(SysJobEntity sysJob) {
		boolean isConcurrent = "0".equals(sysJob.getConcurrent());
		return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
	}

	/**
	 * 构建任务触发对象
	 */
	public static TriggerKey getTriggerKey(Long jobId, String jobGroup) {
		return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
	}

	/**
	 * 构建任务键对象
	 */
	public static JobKey getJobKey(Long jobId, String jobGroup) {
		return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
	}

	/**
	 * 创建定时任务
	 */
	public static void createScheduleJob(Scheduler scheduler, SysJobEntity job) throws SchedulerException, TaskException {
		Class<? extends Job> jobClass = getQuartzJobClass(job);
		// 构建job信息
		Long jobId = job.getId();
		String jobGroup = job.getJobGroup();
		JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();

		// 表达式调度构建器
		CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
		cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);

		// 按新的cronExpression表达式构建一个新的trigger
		CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
				.withSchedule(cronScheduleBuilder).build();

		// 放入参数，运行时的方法可以获取
		jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);

		// 判断是否存在
		if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
			// 防止创建时存在数据问题 先移除，然后在执行创建操作
			scheduler.deleteJob(getJobKey(jobId, jobGroup));
		}

		// 判断任务是否过期
		if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))) {
			// 执行调度任务
			scheduler.scheduleJob(jobDetail, trigger);
		}

		// 暂停任务
		if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
			scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
		}
	}

	/**
	 * 设置定时任务策略
	 */
	public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJobEntity job, CronScheduleBuilder cb)
			throws TaskException {
		switch (job.getMisfirePolicy()) {
		case ScheduleConstants.MISFIRE_DEFAULT:
			return cb;
		case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
			return cb.withMisfireHandlingInstructionIgnoreMisfires();
		case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
			return cb.withMisfireHandlingInstructionFireAndProceed();
		case ScheduleConstants.MISFIRE_DO_NOTHING:
			return cb.withMisfireHandlingInstructionDoNothing();
		default:
			throw new TaskException(
					"The task misfire policy '" + job.getMisfirePolicy() + "' cannot be used in cron schedule tasks",
					Code.CONFIG_ERROR);
		}
	}

	/**
	 * 检查包名是否为白名单配置
	 * 
	 * @param invokeTarget
	 *            目标字符串
	 * @return 结果
	 */
	public static boolean whiteList(String invokeTarget) {
		String packageName = StringUtils.substringBefore(invokeTarget, "(");
		int count = StringUtils.countMatches(packageName, ".");
		if (count > 1) {
			return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR);
		}
		Object obj = null;
		try {
			obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]);
		}catch(NoSuchBeanDefinitionException e){
			AssertHelper.getInstance().checkException(true, "没有找到对应的调用方法");
		} catch (Exception e) {
			AssertHelper.getInstance().checkException(true, e.getMessage());
		}
		String beanPackageName = obj.getClass().getPackage().getName();
		return StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_WHITELIST_STR)
				&& !StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_ERROR_STR);
	}
}
